Note
Click here to download the full example code
filtering block models¶
a block model drawn whole shows its outer shell and hides the grades inside. a layer's filter peels it: a
(low, high) range on a number column, a list of kept categories on a text column, up to four columns that a block
must all pass. the viewer evaluates the filter on the GPU, so the layer's filter panel edits it while viewing with
histogram sliders and category checkboxes, and Scene.filters reads it back or sets it.
import sys
from pathlib import Path
HERE = Path(__file__).parent if "__file__" in globals() else Path.cwd()
sys.path.insert(0, str(HERE.parents[1]))
import boitata as bt
import numpy as np
from common import GRAY, show
zinc composites of the stacked sulphide lenses inform a rotated grid by inverse distance within 60 m; blocks with no
composite in reach stay null and never draw. each block also takes the name of the lens holding its center, or
host. drawn whole, the grid is a brick of low grades.
data = bt.datasets.stacked_sulphide_lenses()
holes = bt.Drillholes.from_tables(data)
lenses = [data[f"lens_{i}"] for i in (1, 2, 3)]
composites = holes.composite(2.0, ["ZN_PCT"])
grid = bt.BlockModel.from_extents(*lenses, size=(10, 10, 5), buffer=20, rotation=(22.5, 0.0, 55.0))
search = bt.Search(radius=60, min_samples=1, max_samples=12)
idw = bt.InverseDistance(search, power=2).fit(composites, "ZN_PCT")
inside = [lens.contains(grid.coords) for lens in lenses]
grid = grid.with_columns(
{
"ZN_PCT": idw.predict(grid),
"lens": list(np.select(inside, [f"lens {i}" for i in (1, 2, 3)], "host")),
}
)
print(f"{len(grid):,} blocks, {np.isnan(grid['ZN_PCT']).mean():.0%} null")
ZN = {"clim": (0, 10), "label": "Zn (%)"}
scene = bt.plot3d.Scene()
scene.add(grid, "ZN_PCT", name="grid", **ZN)
scene.view(azimuth=305, dip=30)
show(scene, "whole", "The whole grid: only its outer shell shows")
Out:
192,888 blocks, 18% null
a range on ZN_PCT keeps the blocks above 4 % and peels the low grades away; the lenses' high-grade cores show
through. an open end is None.
scene = bt.plot3d.Scene()
scene.add(grid, "ZN_PCT", name="grid", filter={"ZN_PCT": (4, None)}, **ZN)
for i, lens in enumerate(lenses, 1):
scene.add(lens, name=f"lens {i}", representation="wireframe", color=GRAY, opacity=0.3)
scene.view(azimuth=305, dip=30)
kept = np.nan_to_num(grid["ZN_PCT"]) >= 4
print(f"{kept.sum():,} of {len(grid):,} blocks at 4 % Zn or more")
show(scene, "grade", "Blocks at 4 % Zn or more")
Out:
7,563 of 192,888 blocks at 4 % Zn or more
a list of categories keeps the blocks of the middle lens; with the grade range too, a block must pass both. here the
filter is set after the layers through filters, by layer name, as the panel would.
scene = bt.plot3d.Scene()
scene.add(grid, "ZN_PCT", name="grid", **ZN)
scene.add(lenses[1], name="lens 2", representation="wireframe", color=GRAY, opacity=0.3)
scene.filters = {"grid": {"lens": ["lens 2"], "ZN_PCT": (2, None)}}
scene.view(azimuth=305, dip=30)
middle = grid["lens"] == "lens 2"
print(f"filters: {scene.filters}")
print(f"{(middle & (np.nan_to_num(grid['ZN_PCT']) >= 2)).sum():,} of {middle.sum():,} blocks of lens 2 shown")
show(scene, "lens", "The blocks of lens 2 at 2 % Zn or more")
Out:
filters: {'grid': {'lens': ['lens 2'], 'ZN_PCT': (2.0, None)}}
1,860 of 3,829 blocks of lens 2 shown
Total running time of the script: ( 1 minutes 0.599 seconds)
Download Python source code: example_02_23.py